package siena.dbslayer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import siena.SienaException;
import siena.logging.SienaLogger;
import siena.logging.SienaLoggerFactory;

/**
 * This is a useful class to work with reflection classes.
 * For example, instance classes, get/set field etc.
 * @author jsanca
 *
 */
public class ReflectionUtil {

	private static ClassLoader classLoader = null;
	
	/**
	 * Set specific classloader.
	 * @param classLoader ClassLoader.
	 */
	public static void setClassLoader(ClassLoader classLoader) {
		
		if (null != classLoader) {
		  ReflectionUtil.classLoader = classLoader;
		}
	} // setClassLoader.
	

	private static SienaLogger logger = SienaLoggerFactory.getLogger(ReflectionUtil.class);
	
	
	public static Field getField (Class<?> clazz, String fieldName) {
	
		Field field = null;
		
		try {
			
			field = clazz.getDeclaredField(fieldName);
		} catch (SecurityException e) {
			
			throw new SienaException (e);
		} catch (NoSuchFieldException e) {
			
			throw new SienaException (e);
		}
		
		return field;
	}
	/**
	 * Get all the declared fields except the class field.
	 * @param clazz Class
	 * @return Collection<Field>
	 */
	public static Collection<Field> getFields (Class<?> clazz) {
		
		Set<Field> fieldSet = new HashSet<Field> ();
		Field[] fields = clazz.getDeclaredFields();
		
		for (Field field : fields) {
		
			if(field.getType() != Class.class) {

				fieldSet.add(field);
			}
		}
		
		return fieldSet;
	} // getFields.
	
	/**
	 * Get the value from the bean. If the bean is null, returns null.
	 * @param field Field
	 * @param bean Object
	 * @return Object the value
	 */
	public static Object getValue (Field field, Object bean) {
		
		Object value = null;
		
		if (null != bean) {
			
			try {
				
				field.setAccessible(true);
				value = field.get(bean);
			} catch (Exception e) {
				
				logger.severe(e, e);
				throw new SienaException(e);
			}
		}
		
		return value;
	} // getValue.
	
	/**
	 * This method determinate if the field has the annotations associated.
	 * @param field Field
	 * @param annotationClass Class
	 * @return boolean true if the field is annotated with the "annotationClass".
	 */
	public static <T extends Annotation> boolean hasAnnotation (Field field, Class<T> annotationClass) {
		
		return null != field.getAnnotation(annotationClass);
	} // hasAnnotation.
	
	/**
	 * Determinate if the class "clazz" has "superClass" argument of super class.
	 * @param clazz Class
	 * @param superClass Class.
	 * @return boolean
	 */
	public static boolean hasSuperClass(Class<?> clazz, Class<?> superClass) {
		
		return clazz.getSuperclass() == superClass;
	} // hasSuperClass.
	
	/**
	 * Create a new instance of the "className", in case that className is null, takes by default defaultClass as instance.
	 * @param className String name of the class to implement
	 * @param defaultClass Class default class
	 * @return return the instance.
	 */
	public static Object getInstance (String className, Class<?> defaultClass)  {
		
		Object instance = null;
		
		try {
			
			instance = (null != className)?
				instance = getInstance(className):
				defaultClass.newInstance();
		} catch (InstantiationException e) {
			
			logger.severe(e, e);
			throw new SienaException (e);
		} catch (IllegalAccessException e) {
			
			logger.severe(e, e);
			throw new SienaException (e);
		}
			
		return instance;	
	} // getInstance.
	/**
	 * Create a new instance of the "className"
	 * @param className String class name of the class.
	 * @return Object
	 * @throws ClassNotFoundException 
	 */
	public static Object getInstance (String className) {
		
		Class<?> clazz = null;
		Object object = null;
		
		try {
			
			clazz = ReflectionUtil.loadClass(className);
			object = clazz.newInstance();
		} catch (ClassNotFoundException e) {

			logger.severe(e, e);
			throw new SienaException (e);
		} catch (InstantiationException e) {
			
			logger.severe(e, e);
			throw new SienaException (e);
		} catch (IllegalAccessException e) {
			
			logger.severe(e, e);
			throw new SienaException (e);
		}
		
		
		return object;
	} // getInstance.
	
	/**
	 * Load from the classpath the Class for name.
	 * @param className String class name of the class.
	 * @return Class
	 * @throws ClassNotFoundException
	 */
	public static Class<?> loadClass (String className) throws ClassNotFoundException {
		
		return (ReflectionUtil.classLoader == null)?
			 Class.forName(className):
		     Class.forName(className, true, ReflectionUtil.classLoader);
	} // getInstance.
} // E:O:F:ReflectionUtil.
